<?php

// * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// * Copyright 2014 The Herosphp Authors. All rights reserved.
// * Use of this source code is governed by a MIT-style license
// * that can be found in the LICENSE file.
// * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

declare(strict_types=1);

namespace herosphp\plugin\storage\handler;

use herosphp\exception\HeroException;
use herosphp\GF;
use herosphp\plugin\storage\core\Error;
use herosphp\plugin\storage\core\UploadError;
use herosphp\utils\FileUtil;

/**
 * 本地文件管理实现
 * --------------------------------------------------
 * @author RockYang<yangjian102621@gmail.com>
 */
class LocalFileHandler implements FileHandler
{
    // 上传目录的最大子目录数
    const MAX_DIR_NUM = 4000;

    protected static array $_config = [];

    public function __construct($config = null)
    {
        if (!isset($config['root'])) {
            throw new HeroException('upload root should not be null.');
        }

        // create upload dir
        if (!file_exists($config['root'])) {
            FileUtil::makeFileDirs($config['root']);
        }

        if (!str_ends_with($config['url'], '/')) {
            $config['url'] = $config['url'] . '/';
        }

        static::$_config = $config;
    }

    public function save(string $srcFile, string $filename): array
    {
        $path = static::_path($filename);
        $dstFile = static::$_config['root'] . DIRECTORY_SEPARATOR . $path;
        // 此处不能用 move_uploaded_file() 函数， 该函数不支持 Workerman 的上传协议
        if (file_exists($srcFile) && rename($srcFile, $dstFile)) {
            return [[
                'path' => $dstFile,
                'url' => static::$_config['url'] . $path
            ], null];
        }

        return [null, Error::get(UploadError::SAVE_FILE_FAIL)];
    }

    public function saveBase64(string $data, string $filename): array
    {
        $image = base64_decode($data);
        if (!$image) {
            return [null, Error::get(UploadError::IMG_DECODE_FAIL)];
        }

        $path = static::_path($filename);
        $dstFile = static::$_config['root'] . DIRECTORY_SEPARATOR . $path;
        if (file_put_contents($dstFile, $data)) {
            return [[
                'path' => $dstFile,
                'url' => static::$_config['url'] . $path
            ], null];
        }

        return [null, Error::get(UploadError::SAVE_FILE_FAIL)];
    }

    public function list(int $page, int $pageNo): array
    {
        throw new HeroException('Not implemented.');
    }

    public function delete(string $filename): array
    {
        $file = static::$_config['root'] . DIRECTORY_SEPARATOR . $filename;
        if (@unlink($file) === false) {
            return [null, new Error(11, "删除文件失败：'{$file}'")];
        }

        return [null, null];
    }

    // generate a relative file path
    protected static function _path(string $key): string
    {
        $hash = GF::bkdrHash($key);
        $dir = $hash % self::MAX_DIR_NUM;
        $path = static::$_config['root'] . DIRECTORY_SEPARATOR . $dir;
        if (!file_exists($path)) {
            mkdir($path);
        }

        return "$dir/$key";
    }
}
